﻿
using Autodesk.AutoCAD.Colors;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Autodesk.AutoCAD.ApplicationServices;
using System.Net;

namespace XS.Tunnel.Cad.SteelBracing.Infrastructure.Extensions
{
    public static class ObjectExtension
    {
        public static List<Entity> ToEntitys(this IList<ISysPoint> points)
        { 
            var list=new List<Entity>();
            foreach (ISysPoint point in points) {

                var entity = point.ToEntity();
                list.Add(entity);
            }
            return list;
        }


        /// <summary>
        /// 弧R 缩进
        /// </summary>
        /// <param name="points"></param>
        /// <param name="L"></param>
        /// <returns></returns>
        public static List<ISysPoint> ArcRetract(this IList<ISysPoint> points, double L)
        {
            var list = new List<ISysPoint>();
            foreach (var item in points)
            {
                if (item is ArcPoint)
                {
                    var m = item as ArcPoint;
                    var s = new ArcPoint(m.OX, m.OY, m.R - L, m.StartAngle, m.EndAngle, m.TotalAngle);
                    s.No = m.No;
                    s.Code = m.Code;
                    s.Name = m.Name;
                    s.BizTpyeCode = m.BizTpyeCode;
                    list.Add(s);
                }
            }
            return list;
        }

        /// <summary>
        /// 弧形缩进
        /// </summary>
        /// <param name="point"></param>
        /// <param name="L"></param>
        /// <returns></returns>
        public static ArcPoint ArcRetract(this ISysPoint point, double L)
        {
            var m = point as ArcPoint;
            var s = new ArcPoint(m.OX, m.OY, m.R - L, m.StartAngle, m.EndAngle, m.TotalAngle);
            s.No = m.No;
            s.Code = m.Code;
            s.Name = m.Name;
            s.BizTpyeCode = m.BizTpyeCode;
            return s;
        }

        /// <summary>
        /// 直线左右移动
        /// </summary>
        /// <param name="point"></param>
        /// <param name="L"></param>
        /// <returns></returns>
        public static LinePoint LineRetract(this ISysPoint point,  double L ,bool isLeft=true)
        {

            var item = point;
            if (item is LinePoint)
            {
                var line = item as LinePoint;
                LinePoint s;
               
                if (isLeft) // 右边   向左移动
                {
                    var k = (line.X1 - line.X) / (line.Y1 - line.Y);
                    // 上
                    var x2 = line.X1 - (L * Math.Cos(Math.Atan(k)));
                    var y2 = line.Y1+ L * Math.Sin(Math.Atan(k));
                    // 下
                    var y1 = line.Y ;
                    var x1 = line.X - (L/Math.Cos(Math.Atan(k)));
                    s = new LinePoint(x1,y1,x2,y2);

                }
                else
                {
                    // 左
                    var k = (line.X1 - line.X) / (line.Y1 - line.Y);

                    var x1 = line.X + (L * Math.Cos(Math.Atan(k)));
                    var y1 = line.Y - L * Math.Sin(Math.Atan(k));

                    var x2 = line.X1 + (L * Math.Cos(Math.Atan(k)));
                    var y2 = line.Y1;
                    s = new LinePoint(x1, y1, x2, y2);
                }
                
                s.No =line .No;
                s.Code = line.Code;
                s.Name = line.Name;
                s.BizTpyeCode = line.BizTpyeCode;
                return s;
            }
            return null;
        }





        /// <summary>
        /// 获取长度
        /// </summary>
        /// <param name="points"></param>
        /// <returns></returns>
        public static double GetLength(this IList<ISysPoint> points)
        {
            double length = 0;
            if (points != null && points.Count > 0)
            {
                foreach (ISysPoint point in points)
                {
                    length += point.Lenght();
                }
            }

            return length;

        }

        /// <summary>
        /// 转换多线段
        /// </summary>
        /// <param name="points"></param>
        /// <returns></returns>
        public static Polyline ToPolyline(this IList<ISysPoint> list,string color="")
        {
            Polyline polyline = new Polyline();
            if (!string.IsNullOrEmpty(color))
            {
                polyline.Color = color.ToColor();
            }

            var points = list.OrderBy(x=>x.No).ToList();

            ISysPoint last = null;
            Point2d? endPoint=null;
            for (int i = 0; i < points.Count; i++)
            {
                var item = points[i];
                double bulge = 0;
                Point2d point;
                if (item is ArcPoint)
                {
                    var arc = (ArcPoint)item;
                    bulge =   Math.Tan((arc.EndAngle-arc.StartAngle).DegreeToAngle() / 4) ;
                    //if (last != null && last is LinePoint)
                    //{
                    //    var l = last as LinePoint;
                    //    point=new Point2d(l.X1, l.Y1);
                    //}
                    //else
                    //{
                    //    point = new Point2d(arc.X0, arc.Y0);
                    //}
                    point = new Point2d(arc.X0, arc.Y0);
                    endPoint = new Point2d(arc.X1,arc.Y1);
                    last = item;
                }
                else
                {
                    var line = (LinePoint)item;
                    point = new Point2d(line.X, line.Y);
                    endPoint=new Point2d(line.X1,line.Y1);
                    last = line;
                }
                polyline.AddVertexAt(i, point, bulge, 0, 0);
                //最后一个
                if (i + 1 == points.Count)
                {
                    polyline.AddVertexAt(i += 1, endPoint.Value, 0, 0, 0);
                }
            }
            return polyline;
        }


        /// <summary>
        /// 转换多线段
        /// </summary>
        /// <param name="points"></param>
        /// <returns></returns>
        public static Polyline ToPolyline2(this IList<ISysPoint> list, string color = "")
        {
            Polyline polyline = new Polyline();
            if (!string.IsNullOrEmpty(color))
            {
                polyline.Color = color.ToColor();
            }

            var points = list.OrderBy(x => x.No).ToList();

            for (int i = 0; i < points.Count; i++)
            {
                var item = points[i];
                double bulge = 0;
                Point2d? startPoint = null;
                Point2d? endPoint = null;
                if (item is ArcPoint)
                {
                    var arc = (ArcPoint)item;
                    bulge = Math.Tan((arc.EndAngle - arc.StartAngle).DegreeToAngle() / 4);

                    // bulge = new Arc(new Point3d(arc.OX, arc.OY, 0), arc.R, arc.StartAngle.DegreeToAngle(), arc.EndAngle.DegreeToAngle()).GetArcBulge();
                    // bulge = new Arc(new Point3d(arc.OX, arc.OY, 0), arc.R, arc.StartAngle.DegreeToAngle(), arc.EndAngle.DegreeToAngle()).GetArcBulge2();
                    //bulge =GetArcBulge(new Point2d(arc.X0, arc.Y0),new Point2d(arc.VerticalX,arc.VerticalY),new Point2d(arc.X1,arc.Y1));

                    //startPoint = new Point2d(arc.X0, arc.Y0);
                    //endPoint = new Point2d(arc.X1, arc.Y1);

                    //var c= startPoint.GetArcBulgeCenter(endPoint, bulge);

                    //var l = new Line(startPoint.To3d(),endPoint.To3d()).Length;

                    //var h = arc.R-Math.Sqrt(Math.Pow(arc.R, 2) - Math.Pow(l / 2, 2));

                    //bulge= Math.Round((2* (2*arc.R-h))/ l,8);

                    startPoint = new Point2d(arc.X0, arc.Y0);
                    endPoint = new Point2d(arc.X1, arc.Y1);


                }
                else
                {
                    var line = (LinePoint)item;
                    startPoint = new Point2d(line.X, line.Y);
                    endPoint = new Point2d(line.X1, line.Y1);
                }
                polyline.AddVertexAt(i, startPoint.Value, bulge, 0, 0);

                //最后一个
                if (i + 1 == points.Count)
                {
                    polyline.AddVertexAt(i += 1, endPoint.Value, 0, 0, 0);
                }
            }
            return polyline;
        }


        /// <summary>
        /// X轴到向量的弧度,cad的获取的弧度是1PI,所以转换为2PI(上小,下大)
        /// </summary>
        /// <param name="ve">向量</param>
        /// <returns>弧度</returns>
        public static double GetAngle2XAxis(this Vector2d ve)
        {
            double alz = Vector2d.XAxis.GetAngleTo(ve);//观察方向不要设置 
            alz = ve.Y > 0 ? alz : Math.PI - alz; //逆时针为正,如果-负值控制正反
            alz = Math.Abs(Math.PI - alz) < 1e-10 ? 0 : alz;
            return alz;
        }

        /// <summary>
        /// X轴到向量的弧度,cad的获取的弧度是1PI,所以转换为2PI(上小,下大)
        /// </summary>
        public static double GetAngle2XAxis(this Point2d startPoint, Point2d endtPoint)
        {
            return startPoint.GetVectorTo(endtPoint).GetAngle2XAxis();
        }


        /// <summary>
        /// 凸度求弧长
        /// </summary>
        /// <param name="arc1">圆弧头点</param>
        /// <param name="arc3">圆弧尾点</param>
        /// <param name="bulge">凸度</param>
        /// <returns></returns>
        public static double GetLength(Point2d arc1, Point2d arc3, double bulge)
        {
            var bowLength = arc1.DistanceTo(arc3);         //弦长
            var bowLength2 = bowLength / 2;                //半弦
            var archHeight = Math.Abs(bulge) * bowLength2; //拱高==凸度*半弦

            //根据三角函数: (弦长/2)²+(半径-拱高)²=半径²
            //再根据:完全平方公式变形: (a+b)²=a²+2ab+b²、(a-b)²=a²-2ab+b²
            var r = (bowLength2 * bowLength2 + archHeight * archHeight) / (2 * archHeight); //半径
                                                                                            //求圆心角:一半圆心角[(对边==半弦长,斜边==半径)]; *2就是完整圆心角
            var asin = Math.Asin(bowLength2 / r) * 2; //反正弦
                                                      //弧长公式: 弧长=绝对值(圆心弧度)*半径...就是单位*比例..缩放过程
            var arcLength = asin * r;
            return arcLength;
        }

        public static double DistanceTo(this Point2d value,Point2d arc3)
        {
            double a = Math.Pow(value.X - arc3.X, 2);//求x的y次方
            double b = Math.Pow(value.Y - arc3.Y, 2);
            return Math.Sqrt(a + b);
        }

        /// <summary>
        /// 圆弧的腰点
        /// </summary>
        /// <param name="arc1">圆弧点1</param>
        /// <param name="arc3">圆弧点3</param>
        /// <param name="bulge">凸度</param>
        /// <returns>返回腰点</returns>
        /// <exception cref="ArgumentNullException"></exception>
        public static Point2d GetArcMidPoint(Point2d arc1, Point2d arc3, double bulge)
        {
            if (bulge == 0)
                throw new ArgumentException("凸度为0,此线是平的");

            var center = GetArcBulgeCenter(arc1, arc3, bulge);
            var angle1 = center.GetVectorTo(arc1).GetAngle2XAxis();
            var angle3 = center.GetVectorTo(arc3).GetAngle2XAxis();
            // 利用边点进行旋转,就得到腰点,旋转角/2
            // 需要注意镜像的多段线
            double angle = angle3 - angle1;
            if (bulge > 0)
            {
                if (angle < 0)
                    angle += Math.PI * 2;
            }
            else
            {
                if (angle > 0)
                    angle += Math.PI * 2;
            }
            return arc1.RotateBy(angle / 2, center);
        }

        /// <summary>
        /// 凸度求圆心
        /// </summary>
        /// <param name="arc1">圆弧头点</param>
        /// <param name="arc3">圆弧尾点</param>
        /// <param name="bulge">凸度</param>
        /// <returns>圆心</returns>
        public static Point2d GetArcBulgeCenter( this Point2d arc1, Point2d arc3, double bulge)
        {
            if (bulge == 0)
                throw new ArgumentNullException("凸度为0,此线是平的");
            var x1 = arc1.X;
            var y1 = arc1.Y;
            var x2 = arc3.X;
            var y2 = arc3.Y;

            var b = (1 / bulge - bulge) / 2;
            var x = (x1 + x2 - b * (y2 - y1)) / 2;
            var y = (y1 + y2 + b * (x2 - x1)) / 2;
            return new Point2d(x, y);
        }



        /// <summary>
        /// 求凸度,判断三点是否一条直线上
        /// </summary>
        /// <param name="arc1">圆弧起点</param>
        /// <param name="arc2">圆弧腰点</param>
        /// <param name="arc3">圆弧尾点</param>
        /// <returns>逆时针为正,顺时针为负</returns>
        public static double GetArcBulge(this Point2d arc1, Point2d arc2, Point2d arc3)
        {
            double dStartAngle = GetAngle2XAxis(arc2, arc1);
            double dEndAngle = GetAngle2XAxis(arc2, arc3);
            //求的P1P2与P1P3夹角
            var talAngle = (Math.PI - dStartAngle + dEndAngle) / 2;
            //凸度==拱高/半弦长==拱高比值/半弦长比值
            //有了比值就不需要拿到拱高值和半弦长值了,因为接下来是相除得凸度
            double bulge = Math.Sin(talAngle) / Math.Cos(talAngle);

            //处理精度
            if (bulge > 0.9999 && bulge < 1.0001)
                bulge = 1;
            else if (bulge < -0.9999 && bulge > -1.0001)
                bulge = -1;
            else if (Math.Abs(bulge) < 1e-10)
                bulge = 0;
            return bulge;
        }


        /// <summary>
        /// 求凸度,判断三点是否一条直线上..慢一点
        /// </summary>
        /// <param name="arc1">圆弧起点</param>
        /// <param name="arc2">圆弧腰点</param>
        /// <param name="arc3">圆弧尾点</param>
        /// <returns>逆时针为正,顺时针为负</returns>
        public static double GetArcBulge(this Arc arc)
        {
            var arc1 = arc.StartPoint;
            var arc2 = arc.GetPointAtDist(arc.GetDistAtPoint(arc.EndPoint) / 2);//圆弧的腰点
            var arc3 = arc.EndPoint;
            return GetArcBulge(arc1.To2d(), arc2.To2d(), arc3.To2d());
        }


        /// <summary>
        /// 求凸度,判断三点是否一条直线上
        /// </summary>
        /// <param name="arc" >圆弧</ param>
        /// <returns></returns>
        public static double GetArcBulge2(this Arc arc)
        {
            //还有一种求凸度方法是tan(圆心角 / 4),但是丢失了方向,
            //再用叉乘来判断腰点方向,从而凸度是否 * -1

            double bulge = Math.Tan(arc.TotalAngle / 4);                             //凸度是圆心角的四分之一的正切
            Point3d midpt = arc.GetPointAtDist(arc.GetDistAtPoint(arc.EndPoint) / 2);//圆弧的腰点
            Vector3d vmid = midpt - arc.StartPoint;                                  //起点到腰点的向量
            Vector3d vend = arc.EndPoint - arc.StartPoint;                           //起点到尾点的向量
            Vector3d vcross = vmid.CrossProduct(vend);                               //叉乘求正负

            //根据右手定则,腰点向量在尾点向量右侧,则叉乘向量Z值为正,圆弧为逆时针
            if (vcross.Z < 0)
                bulge *= -1;

            //处理精度
            if (bulge > 0.9999 && bulge < 1.0001)
                bulge = 1;
            else if (bulge < -0.9999 && bulge > -1.0001)
                bulge = -1;
            else if (Math.Abs(bulge) < 1e-10)
                bulge = 0;
            return bulge;
        }



    }
}
